﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Generic;

using GameServer.Managers;
using GameServer.Virtual_Objects.User;

namespace GameServer.Networking
{
    class NetworkSocket
    {
        private static Socket _Socket;
        private static HashSet<int> _ActiveConnections;
        private static int _Limit = 0;
        private static int _AcceptedConnections = 0;
        private static UDPServer UDPServ = new UDPServer();

        public static bool openSocket(int Port, int Limit)
        {
            try
            {
                _Socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _ActiveConnections = new HashSet<int>();
                _Limit = Limit;

                try
                {
                    if (UDPServ.StartUDPServer())
                    {
                        _Socket.Bind(new IPEndPoint(IPAddress.Any, Port));
                        _Socket.Listen(25);
                        _Socket.BeginAccept(new AsyncCallback(acceptConnection), _Socket);
                        Log.WriteLine("Accepting connections from " + Port);
                        Log.WriteLine("Listening on ports #5350-#5351 for UDP");
                    }
                    else
                    {
                        return false;
                    }
                }
                catch
                {
                    Log.WriteError("Error while setting up asynchronous the socket server for game connections on port " + Port + ".");
                    Log.WriteError("Port " + Port + " could be invalid or in use already.");
                }

                return false;
            }
            catch { return false; }
        }

        public static void CloseSocket()
        {
            try { _Socket.Close(); }
            catch { }

            _Socket = null;
        }

        private static void acceptConnection(IAsyncResult iAr)
        {
            try
            {
                int SocketID = 0;

                for (int I = 1; I < _Limit; I++)
                {
                    if (_ActiveConnections.Contains(I) == false)
                    {
                        SocketID = I;
                        break;
                    }
                }
                if (SocketID > 0)
                {
                    Socket uSocket = ((Socket)iAr.AsyncState).EndAccept(iAr);
                    string IP = uSocket.RemoteEndPoint.ToString().Split(':')[0];
                    try
                    {
                        string IPBanned = new WebClient().DownloadString(Environment.CurrentDirectory + "\\bannedip.txt");
                        if (!IPBanned.Contains(IP))
                        {
                            //if (uSocket.RemoteEndPoint.ToString().Split(':')[0] == "127.0.0.1") return;

                            if (BanManager.isBanned(uSocket.RemoteEndPoint.ToString().Split(':')[0], Dns.GetHostEntry(uSocket.RemoteEndPoint.ToString().Split(':')[0]).HostName) == false)
                            {
                                Log.WriteLine("Accepted connection [" + SocketID + "] from " + uSocket.RemoteEndPoint.ToString().Split(':')[0]);
                                _ActiveConnections.Add(SocketID); _AcceptedConnections++;
                                virtualUser userObject = new virtualUser(_AcceptedConnections, SocketID, uSocket);
                            }
                            else
                            {
                                Log.WriteLine("Refused a connection from: " + uSocket.RemoteEndPoint.ToString().Split(':')[0]);
                                //DB.runQuery("INSERT INTO log_connections (`timestamp`, `server`, `status`, `ip`, `host`) VALUES ('" + Program.currTimeStamp+ "', '"+Config.SERVER_ID.ToString()+"', '-1', '" + uSocket.RemoteEndPoint.ToString().Split(':')[0] + "', '" + Dns.GetHostEntry(uSocket.RemoteEndPoint.ToString().Split(':')[0]).HostName + "');");
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        //Log.WriteError("Error on acceptConnection: " + ex.Message);
                    }
                }
            }
            catch { }
            _Socket.BeginAccept(new AsyncCallback(acceptConnection), _Socket);
        }

        public static void freeSlot(int SocketID, String IPAddress, String Hostname)
        {
            if (_ActiveConnections.Contains(SocketID))
            {
                _ActiveConnections.Remove(SocketID);
                //DB.runQuery("INSERT INTO log_connections (`timestamp`, `server`, `status`, `ip`, `host`) VALUES ('" + Program.currTimeStamp + "', '"+Config.SERVER_ID.ToString()+"', '1', '" + IPAddress + "', '" + Hostname + "');");
            }
        }
    }
}
